package com.dingding.manager.common.dao;

import java.io.Serializable;
import java.util.List;
import java.util.Map;
import org.hibernate.Criteria;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Criterion;
import org.hibernate.criterion.Restrictions;
import org.hibernate.transform.ResultTransformer;
import org.hibernate.transform.Transformers;
import org.springframework.transaction.annotation.Transactional;

import com.dingding.manager.core.util.SpringContextHolder;

/**
 * 通用的数据库操作类
 * @author fengshuonan
 * @date 2016年12月4日 下午5:10:52
 */
@SuppressWarnings("all")
public class Db {

	private static SessionFactory sessionFactory = SpringContextHolder.getBean(SessionFactory.class);
	
	private Db(){}
	
	/**
	 * <p>根据主键id，获取数据库中的单条记录
	 * <p>hibernate状态：持久态
	 * @param id 主键的值
	 * @param clazz 实体类型
	 */
	public static <T> T findById(Serializable id, Class<T> clazz) {
		return currentSession().get(clazz, id);
	}

	/**
	 * <p>添加一条记录到数据库
	 * <p>hibernate状态：持久态
	 * @param object 被添加的实体对象
	 */
	public static Serializable save(Object object) {
		return currentSession().save(object);
	}

	/**
	 * <p>将一个对象从游离态转变为持久态
	 * <p>如果一个对象是持久态没必要调用这个方法
	 * @param object 被改变的对象
	 */
	public static void update(Object object) {
		currentSession().update(object);
	}

	/**
	 * <p>如果一个对象是临时状态，则调用save(),如果是游离状态则调用update()
	 * <p>hibernate状态：持久态
	 * @param object 被改变的对象
	 */
	public static void saveOrUpdate(Object object) {
		currentSession().saveOrUpdate(object);
	}

	/**
	 * <p>删掉数据库中的一条记录
	 * <p>hibernate状态：临时状态
	 * @param object 被改变的对象
	 */
	public static void delete(Object object) {
		currentSession().delete(object);
	}

	/**
	 * 查询某个实体类数据库中的所有记录
	 * <p>hibernate状态：持久态
	 * @param clazz 实体类型
	 */
	public static <T> List<T> queryAll(Class<T> clazz){
		return currentSession().createCriteria(clazz).list();
	}
	
	/**
	 * 根据Criterion条件查询数据库中相应的记录 <br>
	 * 获取查询条件，请参考{@link Restrictions}
	 * <p>
	 * 实例:<br>
	 * 查询userlogin表中所有性别为'男'的记录<br>
	 * List＜UserLogin＞ list = Db.queryCriterionList(UserLogin.class,Restrictions.eq("sex", "男"));<br><br>
	 * 
	 * Restriction常用条件如下：<br>
	 * 
	 * <pre>
	 * Restrictions.eq         ＝   <br>
	 * Restrictions.gt         ＞   <br>
	 * Restrictions.ge         ＞＝   <br>
	 * Restrictions.lt         ＜   <br>
	 * Restrictions.le         ＜＝   <br>
	 * Restrictions.between    between <br>
	 * Restrictions.like       like   <br>
	 * Restrictions.in         in   <br>
	 * Restrictions.and        and   <br>
	 * Restrictions.or         or   <br>
	 * </pre>
	 * 
	 * @param clazz 实体类型
	 * @param criterions 查询条件
	 */
	public static <T> List<T> queryCriterionList(Class<T> clazz, Criterion... criterions) {
		Criteria criteria = createCriteria(clazz, criterions);
		return criteria.list();
	}

	/**
	 * 根据Criterion条件查询数据库中相应的第一条记录 <br>
	 * 参考：{@link Db#queryCriterionList}
	 * 
	 * @param clazz 实体类型
	 * @param criterions 查询条件
	 */
	public static <T> T queryCriterionFirst(Class<T> clazz,Criterion... criterions){
		List<T> list = queryCriterionList(clazz,criterions);
		if(list != null && list.size() > 0){
			return list.get(0);
		}else{
			return null;
		}
	}
	
	/**
	 * 根据Criterion条件，分页查询数据中相应的记录 <br>
	 * 关于Criterion，请参考：{@link Db#queryCriterionList}<br>
	 * <br>
	 * 实例:<br>
	 * 查询userlogin表中的所有性别为男的所有记录，返回第1页数据，每页20条记录<br>
	 * Page＜UserLogin＞ page = Db.queryCriterionByPage(UserLogin.class,new Page＜UserLogin＞(1,20),Restrictions.eq("sex", "男"));<br><br>
	 * 
	 * @param clazz 实体类型
	 * @param page 分页条件
	 * @param criterions 查询条件
	 */
	public static <T> Page<T> queryCriterionByPage(Class<T> clazz,Page<T> page,Criterion... criterions){
		Criteria criteria = createCriteria(clazz,criterions);
		int totalRow = criteria.list().size();
		page.setTotalRow(totalRow);
		page.setTotalPage(totalRow % page.getPageSize() == 0 ? totalRow/page.getPageSize() : totalRow/page.getPageSize() + 1);
		if(page.getOrderBy() != null){
			criteria.addOrder(page.getOrderBy());
		}
		if(page.getPageNo() != -1 && page.getPageSize() != -1){
			criteria.setFirstResult((page.getPageNo() - 1) * page.getPageSize());
			criteria.setMaxResults(page.getPageSize());
		}
		page.setList(criteria.list());
		return page;
	}
	
	
	/**
	 * 根据sql，查询数据库中相应的记录 <br>
	 * @param clazz 实体类型
	 * @param sql 查询语句
	 * @param params 条件参数
	 */
	public static <T> List<T> querySqlList(Class<T> clazz,String sql,Object... params){
		SQLQuery sqlQuery = sqlQuery(sql,params);
		sqlQuery.setResultTransformer(Transformers.aliasToBean(clazz));
		return sqlQuery.list();
	}
	
	/**
	 * sql查询，并把结果封装为Object[]，请参考{@link Db#querySqlToObjArr(String, Object...)}
	 */
	public static <T> List<T> querySqlBasicTranslationList(Class<T> clazz,String sql,Object... params) {
		SQLQuery sqlQuery = sqlQuery(sql,params);
		return sqlQuery.list();
	}
	
	/**
	 * 根据sql，查询数据库中相应的第一条记录 <br>
	 * @param clazz 实体类型
	 * @param sql 查询语句
	 * @param params 条件参数
	 */
	public static <T> T querySqlFirst(Class<T> clazz,String sql,Object... params){
		List<T> list = querySqlList(clazz,sql,params);
		if(list != null && list.size() > 0){
			return list.get(0);
		}else{
			return null;
		}
	}
	
	/**
	 * 根据sql，查询数据库中相应的记录,并作分页 <br>
	 * 参考：{@link Db#queryCriterionByPage(Class, Page, Criterion...)}
	 * @param clazz 实体类型
	 * @param page 分页条件
	 * @param sql 查询语句
	 * @param params 条件参数
	 */
	public static <T> Page<T> querySqlByPage(Class<T> clazz,Page<T> page,String sql,Object... obj){
		SQLQuery sqlQuery = sqlQuery(sql, obj);
		int totalRow = sqlQuery.list().size();
		page.setTotalRow(totalRow);
		page.setTotalPage(totalRow % page.getPageSize() == 0 ? totalRow/page.getPageSize() : totalRow/page.getPageSize() + 1);
		if(page.getPageNo() != -1 && page.getPageSize() != -1){
			sqlQuery.setFirstResult((page.getPageNo() - 1) * page.getPageSize());
			sqlQuery.setMaxResults(page.getPageSize());
		}
		sqlQuery.setResultTransformer(Transformers.aliasToBean(clazz));
		page.setList(sqlQuery.list());
		return page;
	}
	
	/**
	 * 根据sql语句，查询数据库中的记录，返回结果封装为Object[]形式 <br>
	 * <br>
	 * 实例:<br>
	 * 查询userlogin表中某两列的所有记录<br>
	 * List＜Object[]＞ list = Db.querySqlToObjArr("select u.userName,u.sex from userlogin u");<br><br>
	 * 
	 * 结果:<br>
	 * <pre>
	 * list.get(0)[0];  -->>  第1条记录的userName<br>
	 * list.get(0)[1];  -->>  第1条记录的sex<br>
	 * list.get(1)[0];  -->>  第2条记录的userName<br>
	 * list.get(2)[1];  -->>  第2条记录的sex<br>
	 * </pre>
	 * 
	 * @param sql 查询语句
	 * @param obj 条件参数
	 */
	public static List<Object[]> querySqlToObjArr(String sql,Object... obj){
		SQLQuery sqlQuery = sqlQuery(sql, obj);
		return sqlQuery.list();
	}
	
	/**
	 * 根据sql语句，查询数据库中的记录，返回结果封装为Map形式 <br>
	 * <br>
	 * 实例:<br>
	 * 查询userlogin表中某两列的所有记录<br>
	 * List＜Map＜String,Object＞＞ list = Db.querySqlToMap("select u.userName,u.sex from userlogin u");<br><br>
	 * 
	 * 结果:<br>
	 * <pre>
	 * list.get(0).get("userName");  -->>  第1条记录的userName<br>
	 * list.get(0).get("sex");  	 -->>  第1条记录的sex<br>
	 * list.get(1).get("userName");  -->>  第2条记录的userName<br>
	 * list.get(2).get("sex");  	 -->>  第2条记录的sex<br>
	 * </pre>
	 * 
	 * @param sql 查询语句
	 * @param obj 条件参数
	 */
	public static List<Map<String,Object>> querySqlToMap(String sql,Object... obj){
		SQLQuery sqlQuery = sqlQuery(sql, obj);
		sqlQuery.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		return sqlQuery.list();
	}
	
	/**
	 * 根据sql语句，查询相应表中的第一条记录，返回结果封装为Map形式 <br>
	 * 实例参考：{@link Db#querySqlToMap(String, Object...)}<br>
	 * 
	 * @param sql 查询语句
	 * @param obj 条件参数
	 */
	public static Map<String,Object> querySqlToMapFirst(String sql,Object... obj){
		SQLQuery sqlQuery = sqlQuery(sql, obj);
		sqlQuery.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
		List list = sqlQuery.list();
		if(list == null || list.size() == 0){
			return null;
		}else{
			return (Map<String, Object>) list.get(0);
		}
	}
	
	/**
	 * 如果本类没有满足你的数据库操作，直接调用本方法，再进行hibernate操作
	 */
	public static Session currentSession() {
		return sessionFactory.getCurrentSession();
	}
	
	private static <T> Criteria createCriteria(Class<T> clazz, Criterion... criterions) {
		Criteria criteria = currentSession().createCriteria(clazz);
		for (Criterion one : criterions) {
			criteria.add(one);
		}
		return criteria;
	}
	
	private static SQLQuery sqlQuery(String sql,Object[] params){
		SQLQuery sqlQuery = currentSession().createSQLQuery(sql);
		if(sql.indexOf("?") != -1){
			for(int i=0;i<params.length;i++){
				sqlQuery.setParameter(i, params[i]);
			}
		}
		return sqlQuery;
	}
}
